﻿/* Copyright (C) 2008 - 2011 by John Cronin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace tysila
{
    partial class Assembler
    {
        private bool provides_intcall(MethodToCompile mtc)
        {
            InstructionLine instr = new InstructionLine();
            instr.stack_before = new List<PseudoStack>();
            for (int i = 0; i < get_arg_count(mtc.msig); i++)
                instr.stack_before.Add(new PseudoStack { contains_variable = var.Null });
            return enc_intcall(instr, mtc.meth, mtc.msig, mtc.type, mtc.tsigp, new AssemblerState(), true);
        }

        private bool enc_intcall(InstructionLine i, Metadata.MethodDefRow mdr, Signature.BaseMethod msig, Metadata.TypeDefRow tdr, Signature.Param tsig, AssemblerState state)
        { return enc_intcall(i, mdr, msig, tdr, tsig, state, false); }
        private bool enc_intcall(InstructionLine i, Metadata.MethodDefRow mdr, Signature.BaseMethod msig, Metadata.TypeDefRow tdr, Signature.Param tsig, AssemblerState state, bool provides)
        {
            string methname;
            bool handled = true;
            bool i_pushes_set = false;

            Assembler.TypeToCompile ttc = new TypeToCompile { _ass = this, tsig = tsig, type = tdr };

            methname = Mangler.Mangle(tdr, tsig, mdr, msig, this);
            methname = methname.Substring(Mangler.Mangle(tdr.m).Length);

            if (methname == "_N6_System_T6_Object_S2_1C_M19_InternalGetHashCode_S8_0001081C")
            {
                var obj_var = i.stack_before[i.stack_before.Count - 1].contains_variable;
                if (obj_var.type != var.var_type.LogicalVar)
                {
                    i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++, obj_var, var.Null));
                    obj_var = state.next_variable - 1;
                }
                var obj_id_var = state.next_variable++;

                int fld_offset = Layout.GetLayout(ttc, this).GetField("Int32 __object_id", false).offset;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, obj_id_var, var.ContentsOf(obj_var, fld_offset), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, state.next_variable++, obj_id_var,
                    var.Const((UInt32)0x1E3779B1)));
            }
            else if (methname == "_N6_System_T6_Object_S2_1C_M7_GetType_S10_2000128128")
            {
                var obj_var = i.stack_before[i.stack_before.Count - 1].contains_variable;
                if (obj_var.type != var.var_type.LogicalVar)
                {
                    i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++, obj_var, var.Null));
                    obj_var = state.next_variable - 1;
                }
                var vtbl_ptr_var = state.next_variable++;

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, vtbl_ptr_var,
                    var.ContentsOf(obj_var), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++,
                    var.ContentsOf(vtbl_ptr_var), var.Null));
            }
            else if (methname == "_N6_System_T6_Object_S2_1C_M15_MemberwiseClone_S6_20001C")
            {
                /* Returns a direct copy of the current object
                 * 
                 * First allocate a new memory block of the correct size
                 * If the object is not a string type, then get the class size from the TypeInfo structure
                 * If it is a string type: load its length, multiply by sizeof(char), add String.data_offset
                 * 
                 * Then set the object_id to something new
                 */

                var v_obj = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_str_ti = state.next_variable++;
                var v_obj_vt = state.next_variable++;
                var v_obj_ti = state.next_variable++;
                var v_length = state.next_variable++;
                var v_newobj = state.next_variable++;
                var v_objid = state.next_variable++;

                int blk_notstring = state.next_block++;
                int blk_lengthfound = state.next_block++;

                TypeToCompile str_ttc = Metadata.GetTTC(new Signature.Param(BaseType_Type.String), new TypeToCompile { _ass = this, tsig = tsig, type = tdr }, null, this);

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_obj_vt, var.ContentsOf(v_obj, 0), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_obj_ti, var.ContentsOf(v_obj_vt, 0), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_str_ti, var.AddrOfObject(Mangler.Mangle(str_ttc, this) + "_TI"), var.Null));
                Requestor.RequestTypeInfo(str_ttc);
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i, var.Null, v_obj_vt, v_str_ti));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.bne, blk_notstring));

                /* It is a string */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_length, var.ContentsOf(v_obj, GetStringFieldOffset(StringFields.length)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_length, v_length, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i4, v_length, v_length, var.Const(GetStringFieldOffset(StringFields.data_offset))));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.br, blk_lengthfound));

                /* Its not a string */
                i.tacs.Add(LabelEx.LocalLabel(blk_notstring));
                GetTysosTypeLayout();
                var v_globallength = v_length;
                v_globallength.is_global = true;        // this prevents the assembler from marking the variable dead and stops it removing the above code which
                                                        // also assigns to v_length
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_globallength, var.ContentsOf(v_obj_ti, tysos_type_offsets["Int32 ClassSize"]), var.Null));

                /* Get the memory and do the memcpy */
                i.tacs.Add(LabelEx.LocalLabel(blk_lengthfound));
                i.tacs.Add(new CallEx(v_newobj, new var[] { v_length }, "gcmalloc", callconv_gcmalloc));
                i.tacs.Add(new CallEx(var.Null, new var[] { v_newobj, v_obj, v_length }, "__memcpy", callconv_memcpy));

                /* Store the new object id */
                i.tacs.Add(new CallEx(v_objid, new var[] { }, "__get_new_obj_id", callconv_getobjid));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, var.ContentsOf(v_newobj, GetStringFieldOffset(StringFields.objid)), v_objid, var.Null));

                i.pushes_variable = v_newobj;
                i_pushes_set = true;
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M6_PeekU1_S8_0001050B")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.peek_u1, state.next_variable++,
                    i.stack_before[i.stack_before.Count - 1].contains_variable, 0));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M6_PeekU2_S8_0001070B")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.peek_u2, state.next_variable++,
                    i.stack_before[i.stack_before.Count - 1].contains_variable, 0));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M6_PeekU4_S8_0001090B")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.peek_u4, state.next_variable++,
                    i.stack_before[i.stack_before.Count - 1].contains_variable, 0));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M6_PeekU8_S8_00010B0B")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.peek_u8, state.next_variable++,
                    i.stack_before[i.stack_before.Count - 1].contains_variable, 0));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M4_Poke_S10_0002010B05")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.poke_u1, 0,
                    i.stack_before[i.stack_before.Count - 2].contains_variable,
                    i.stack_before[i.stack_before.Count - 1].contains_variable));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M4_Poke_S10_0002010B07")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.poke_u2, 0,
                    i.stack_before[i.stack_before.Count - 2].contains_variable,
                    i.stack_before[i.stack_before.Count - 1].contains_variable));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M4_Poke_S10_0002010B09")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.poke_u4, 0,
                    i.stack_before[i.stack_before.Count - 2].contains_variable,
                    i.stack_before[i.stack_before.Count - 1].contains_variable));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M4_Poke_S10_0002010B0B")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.poke_u8, 0,
                    i.stack_before[i.stack_before.Count - 2].contains_variable,
                    i.stack_before[i.stack_before.Count - 1].contains_variable));
            }
            else if (methname == "_N3_ABI_T16_MemoryOperations_Special_M4_Poke_S10_0002010B19")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.poke_u, 0,
                    i.stack_before[i.stack_before.Count - 2].contains_variable,
                    i.stack_before[i.stack_before.Count - 1].contains_variable));
            }
            else if (methname == "_N3_ABI_T12_IoOperations_Special_M7_PortOut_S10_0002010705")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.portout_u2_u1, 0,
                    i.stack_before[i.stack_before.Count - 2].contains_variable,
                    i.stack_before[i.stack_before.Count - 1].contains_variable));
            }
            else if (methname == "_N3_ABI_T12_IoOperations_Special_M7_PortInb_S8_00010507")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.portin_u2_u1, state.next_variable++,
                    i.stack_before[i.stack_before.Count - 1].contains_variable, var.Null));
            }
            else if (methname.StartsWith("_N3_ABI_T16_MemoryOperations_Special_M16_GetInternalArray_S"))
            {
                /* static void *GetInternalArray(System.Array array) */

                var v_ret = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_ret, i.stack_before[i.stack_before.Count - 1].contains_variable, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_ret, var.ContentsOf(v_ret, GetArrayFieldOffset(ArrayFields.inner_array)), var.Null));
            }
            else if (Metadata.IsSpecialType(tdr, tsig.Type, this) && mdr.Name.StartsWith("ReinterpretAs"))
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++,
                    i.stack_before[i.stack_before.Count - 1].contains_variable, 0));
            }
            else if (methname.StartsWith("_N3_ABI_T14_CastOperations_Special_M9_GetArg"))
            {
                char arg_c = methname["_N3_ABI_T14_CastOperations_Special_M9_GetArg".Length];
                var la = var.LocalArg(Int32.Parse(new String(new char[] { arg_c })));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++, la, var.Null));
            }
            else if (methname == "_N6_System_T5_Array_S6_12831C_M9_GetLength_S8_20010808")
            {
                /* For this and the next two functions on System.Array, we need to create a super class e.g. int[]
                 * that defines the fields we access (lobounds/sizes/rank/elem_size/inner_array) and then get the field offsets from it
                 * This is safe because all ComplexArrays define these fields in the same location, and System.Array
                 * is marked abstract and so it must be extended by a ComplexArray.
                 * 
                 * This is accomplished in Assembler_Array.cs
                 * 
                 * TODO: can we prove that the only thing it is extended by is a ComplexArray???
                 */

                var v_sizes = state.next_variable++;
                var v_rank = state.next_variable++;
                var v_obj = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_dimension = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_offset = state.next_variable++;
                var v_offset_i = state.next_variable++;

                enc_checknullref(i, v_obj);

                // Range check
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_rank, var.ContentsOf(v_obj, GetArrayFieldOffset(ArrayFields.rank)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_dimension, v_rank));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.throwge_un, var.Null, var.Const(throw_IndexOutOfRangeException), var.Null));

                // Load the correct element within the sizes array (== array_start + dimension * packedsizeof(I4))
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_sizes, var.ContentsOf(v_obj, GetArrayFieldOffset(ArrayFields.sizes)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_offset, v_dimension, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_offset, v_offset, var.Const(GetPackedSizeOf(new Signature.Param(BaseType_Type.I4)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_isx, v_offset_i, v_offset, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_sizes, v_sizes, v_offset));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, state.next_variable++, var.ContentsOf(v_sizes), var.Null));
            }
            else if (methname == "_N6_System_T5_Array_S6_12831C_M7_GetRank_S6_200008")
            {
                var v_obj = i.stack_before[i.stack_before.Count - 1].contains_variable;

                enc_checknullref(i, v_obj);
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, state.next_variable++, var.ContentsOf(v_obj, GetArrayFieldOffset(ArrayFields.rank)), var.Null));
            }
            else if (methname == "_N6_System_T5_Array_S6_12831C_M13_GetLowerBound_S8_20010808")
            {
                var v_lobounds = state.next_variable++;
                var v_rank = state.next_variable++;
                var v_obj = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_dimension = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_offset = state.next_variable++;
                var v_offset_i = state.next_variable++;

                enc_checknullref(i, v_obj);

                // Range check
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_rank, var.ContentsOf(v_obj, GetArrayFieldOffset(ArrayFields.rank)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_dimension, v_rank));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.throwge_un, var.Null, var.Const(throw_IndexOutOfRangeException), var.Null));

                // Load the correct element within the sizes array (== dimension * packedsizeof(I4))
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_lobounds, var.ContentsOf(v_obj, GetArrayFieldOffset(ArrayFields.lobounds)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_offset, v_dimension, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_offset, v_offset, var.Const(GetPackedSizeOf(new Signature.Param(BaseType_Type.I4)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_isx, v_offset_i, v_offset, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_lobounds, v_lobounds, v_offset));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, state.next_variable++, var.ContentsOf(v_lobounds), var.Null));
            }
            else if (methname == "_N6_System_T5_Array_S6_12831C_M12_GetValueImpl_S8_20011C08")
                return false;
            else if (methname == "_N6_System_T5_Array_S6_12831C_M12_SetValueImpl_S10_2002011C08")
                return false;
            else if (methname == "_N6_System_T5_Array_S6_12831C_M8_GetValue_S10_20011C1D08")
                return false;
            else if (methname == "_N6_System_T5_Array_S6_12831C_M8_SetValue_S12_2002011C1D08")
                return false;
            else if (methname == "_N6_System_T5_Array_S6_12831C_M8_FastCopy_S24_00050212831C0812831C0808")
            {
                /* static bool System.Array.FastCopy(Array sourceArray, int sourceIndex, Array destinationArray,
                 *                                      int destinationIndex, int length)
                 *                                      
                 * perform a copy between source and dest
                 * return false if this cannot be done quickly (e.g. the underlying types do not match)
                 * if sourceArray == destinationArray perform a memmove (safely move overlapping arrays) if memcpy
                 * would destroy data (sourceIndex < destIndex)
                 * 
                 * code is:
                 * 
                 * nullrefchecks
                 * 
                 * compare elemtypes
                 * retval = false
                 * jne .end
                 * 
                 * retval = true
                 * cmp length, 0
                 * je .end
                 * 
                 * check sourceIndex + length <= sourceArray.__inner_array_bytelength
                 * check destinationIndex + length <= destinationArray.__inner_array_bytelength
                 * 
                 * calculate start offsets in both internal arrays
                 * calculate byte length
                 * 
                 * cmp sourceArray, destArray
                 * jne .memcpy
                 * cmp sourceIndex, destIndex
                 * ja .memcpy
                 * je .end      // if ((sourceArray == destArray) && (sourceIndex == destIndex)) do nothing
                 * call(__memmove)
                 * jmp .end
                 * .memcpy
                 * call(__memcpy)
                 * .end
                 */

                var v_length = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_destIndex = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_destArray = i.stack_before[i.stack_before.Count - 3].contains_variable;
                var v_srcIndex = i.stack_before[i.stack_before.Count - 4].contains_variable;
                var v_srcArray = i.stack_before[i.stack_before.Count - 5].contains_variable;
                var v_retval = state.next_variable++;
                int blk_end = state.next_block++;
                int blk_memcpy = state.next_block++;

                enc_checknullref(i, v_srcArray);
                enc_checknullref(i, v_destArray);

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_retval, var.Const(0), var.Null));

                var v_srcET = state.next_variable++;
                var v_destET = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_srcET, var.ContentsOf(v_srcArray, GetArrayFieldOffset(ArrayFields.elemtype)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_destET, var.ContentsOf(v_destArray, GetArrayFieldOffset(ArrayFields.elemtype)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i, var.Null, v_srcET, v_destET));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.bne, blk_end));

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_retval, var.Const(-1), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_length, var.Const(0)));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.beq, blk_end));

                var v_srcEnd = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i4, v_srcEnd, v_srcIndex, v_length));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_srcEnd, var.ContentsOf(v_srcArray, GetArrayFieldOffset(ArrayFields.inner_array_length), 4)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.throwg_un, var.Null, var.Const(throw_IndexOutOfRangeException), var.Null));
                var v_destEnd = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i4, v_destEnd, v_destIndex, v_length));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_destEnd, var.ContentsOf(v_destArray, GetArrayFieldOffset(ArrayFields.inner_array_length), 4)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.throwg_un, var.Null, var.Const(throw_IndexOutOfRangeException), var.Null));

                var v_srcIntIndex = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_srcIntIndex, v_srcIndex, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_srcIntIndex, v_srcIntIndex, var.ContentsOf(v_srcArray, GetArrayFieldOffset(ArrayFields.elem_size), 4)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_isx, v_srcIntIndex, v_srcIntIndex, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_srcIntIndex, v_srcIntIndex, var.ContentsOf(v_srcArray, GetArrayFieldOffset(ArrayFields.inner_array))));
                var v_destIntIndex = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_destIntIndex, v_destIndex, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_destIntIndex, v_destIntIndex, var.ContentsOf(v_destArray, GetArrayFieldOffset(ArrayFields.elem_size), 4)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_isx, v_destIntIndex, v_destIntIndex, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_destIntIndex, v_destIntIndex, var.ContentsOf(v_destArray, GetArrayFieldOffset(ArrayFields.inner_array))));
                var v_bytelength = state.next_variable++;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_bytelength, v_length, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_bytelength, v_bytelength, var.ContentsOf(v_srcArray, GetArrayFieldOffset(ArrayFields.elem_size), 4)));

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i, var.Null, v_srcArray, v_destArray));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.bne, blk_memcpy));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_srcIndex, v_destIndex));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.ba, blk_memcpy));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.beq, blk_end));

                i.tacs.Add(new CallEx(var.Null, new var[] { v_destIntIndex, v_srcIntIndex, v_bytelength }, "__memmove", callconv_memmove));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.br, blk_end));

                i.tacs.Add(LabelEx.LocalLabel(blk_memcpy));
                i.tacs.Add(new CallEx(var.Null, new var[] { v_destIntIndex, v_srcIntIndex, v_bytelength }, "__memcpy", callconv_memcpy));

                i.tacs.Add(LabelEx.LocalLabel(blk_end));
                i.pushes_variable = v_retval;
                i_pushes_set = true;
            }
            else if (methname == "_N6_System_T5_Array_S6_12831C_M18_CreateInstanceImpl_S24_000312831C1284F81D081D08")
                return false;
            else if (methname == "_N6_System_T5_Array_S6_12831C_M13_ClearInternal_S16_00030112831C0808")
                return false;
            else if (methname == "_N6_System_T5_Array_S6_12831C_M5_Clone_S6_20001C")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M10_get_Length_S6_200008")
            {
                var obj_var_id = i.stack_before[i.stack_before.Count - 1].contains_variable;
                enc_checknullref(i, obj_var_id);

                // length = *(obj_var_id + length_offset)
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, state.next_variable++, var.ContentsOf(obj_var_id, GetStringFieldOffset(StringFields.length)), 0));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M9_get_Chars_S8_20010308")
            {
                // char get_Chars(int32 idx)
                // if(idx >= length) throw IndexOutOfRangeException
                // if(idx < 0) throw IndexOutOfRangeException
                // addr = obj + obj.ClassSize + idx * char.Size
                // retval = peek_u2(addr)

                var v_str = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_idx = i.stack_before[i.stack_before.Count - 1].contains_variable;

                var v_length = state.next_variable++;
                var v_addr = state.next_variable++;
                var v_offset = state.next_variable++;

                enc_checknullref(i, v_str);

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_length, var.ContentsOf(v_str, GetStringFieldOffset(StringFields.length)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_idx, v_length));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.throwge_un, var.Null, var.Const(throw_IndexOutOfRangeException), var.Null));

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_offset, v_idx, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_offset, v_offset, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i4, v_offset, v_offset, var.Const(GetStringFieldOffset(StringFields.data_offset))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_addr, v_offset, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_addr, v_addr, v_str));

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.peek_u2, state.next_variable++, v_addr, var.Null));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M19_InternalAllocateStr_S8_00010E08")
            {
                /* static string InternalAllocateString(int32 length)
                 * 
                 * v_ret = gcmalloc(sizeof(string) + length * sizeof(char))
                 * *(v_ret + __vtbl_offset) = System.String_VT
                 * *(v_ret + __obj_id) = next_object_id++
                 * *(v_ret + length) = length
                 */

                Assembler.TypeToCompile ttc_string = Metadata.GetTTC("mscorlib", "System", "String", this);
                Layout l = Layout.GetLayout(ttc_string, this);

                var v_ret = state.next_variable++;
                var v_size = state.next_variable++;
                var v_length = state.next_variable++;
                var v_mem_size = state.next_variable++;

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_size, var.Const(GetStringFieldOffset(StringFields.data_offset)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_length, i.stack_before[i.stack_before.Count - 1].contains_variable, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_length, v_length, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i4, v_size, v_size, v_length));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_isx, v_mem_size, v_size, var.Null));
                i.tacs.Add(new CallEx(v_ret, new var[] { v_mem_size }, "gcmalloc", callconv_gcmalloc));

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, var.ContentsOf(v_ret, GetStringFieldOffset(StringFields.vtbl)), var.AddrOfObject(l.vtable_object_name), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, var.ContentsOf(v_ret, GetStringFieldOffset(StringFields.objid)), var.Const(next_object_id.Increment), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, var.ContentsOf(v_ret, GetStringFieldOffset(StringFields.length)), i.stack_before[i.stack_before.Count - 1].contains_variable, var.Null));

                i.pushes_variable = v_ret;
                i_pushes_set = true;
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S10_2001010F04")
            {
                /* instance void .ctor(sbyte * val)
                 * 
                 * The string should be already set up to the correct length by InternalAllocateStr
                 * 
                 * We define a conversion function __mbstowcs(wchar_t *wcstr, const char *mbstr, size_t count)
                 * where count is the maximum number of characters to convert.
                 * For safety's sake, we set this to be the length of the string object we are converting into.
                 */

                var v_str_len = state.next_variable++;
                var v_str_data = state.next_variable++;
                var v_src_data = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_obj = i.stack_before[i.stack_before.Count - 2].contains_variable;

                enc_checknullref(i, v_src_data);
                enc_checknullref(i, v_obj);

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_str_len, var.ContentsOf(v_obj, GetStringFieldOffset(StringFields.length)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_str_data, v_obj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_str_data, v_str_data, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));
                i.tacs.Add(new CallEx(var.Null, new var[] { v_str_data, v_src_data, v_str_len }, "__mbstowcs", callconv_mbstowcs));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S10_2001010F03")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S14_2003010F030808")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S14_2003010F040808")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S20_2004010F040808129998")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S14_2003011D030808")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S10_2001011D03")
                return false;
            else if (methname == "_N6_System_T6_String_S2_0E_M7_#2Ector_S10_2002010308")
                return false;
            else if (methname == "_N35_System#2ERuntime#2ECompilerServices_T14_RuntimeHelpers_S6_128D24_M22_get_OffsetToStringData_S6_000008")
            {
                /* static int32 get_OffsetToStringData()
                 * 
                 * ret sizeof(string)
                 */

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, state.next_variable++, var.Const(GetStringFieldOffset(StringFields.data_offset)), var.Null));
            }
            else if (methname == "_N35_System#2ERuntime#2ECompilerServices_T14_RuntimeHelpers_S6_128D24_M15_InitializeArray_S14_00020112831C18")
            {
                /* static void InitializeArray(System.Array array, native int FieldInfo)
                 * 
                 * memcpy(array->inner_array, FieldInfo->Literal_data, array->bytesize)
                 */

                var v_array = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_fieldinfo = i.stack_before[i.stack_before.Count - 1].contains_variable;

                var v_elemsize = state.next_variable++;
                var v_arraylength = state.next_variable++;
                var v_intarray = state.next_variable++;
                var v_litdata = state.next_variable++;

                enc_checknullref(i, v_array);
                enc_checknullref(i, v_fieldinfo);

                /* calculate the byte length of the inner array */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_elemsize, var.ContentsOf(v_array, GetArrayFieldOffset(ArrayFields.elem_size)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_arraylength, var.ContentsOf(v_array, GetArrayFieldOffset(ArrayFields.inner_array_length)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_arraylength, v_arraylength, v_elemsize));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_isx, v_arraylength, v_arraylength, var.Null));

                /* get the inner array address */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_intarray, var.ContentsOf(v_array, GetArrayFieldOffset(ArrayFields.inner_array)), var.Null));

                /* get the literal data */
                GetTysosFieldLayout();
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_litdata, var.ContentsOf(v_fieldinfo, tysos_field_offsets["IntPtr Literal_data"]), var.Null));
                enc_checknullref(i, v_litdata);

                /* call memcpy to set up the array */
                i.tacs.Add(new CallEx(var.Null, new var[] { v_intarray, v_litdata, v_arraylength }, "__memcpy", callconv_memcpy));
            }
            else if (methname == "_N6_System_T11_Environment_S6_1284DC_M11_get_NewLine_S6_00000E")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, state.next_variable++,
                    var.Const((ushort)0x000a), 0));
            }
            else if (methname == "_N18_System.Diagnostics_T10_StackTrace_S6_128780_M9_get_trace_S22_00031D12877C1281300802")
            {
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++,
                    var.Const(0L), var.Null));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M14_InternalStrcpy_S14_0003010E081D03")
            {
                /* static void InternalStrcpy(string dest, int32 destPos, char[] chars) */

                var v_dest = i.stack_before[i.stack_before.Count - 3].contains_variable;
                var v_destPos = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_chars = i.stack_before[i.stack_before.Count - 1].contains_variable;

                var v_actdest = state.next_variable++;
                var v_actsrc = state.next_variable++;
                var v_length = state.next_variable++;
                var v_destposadj = state.next_variable++;

                enc_checknullref(i, v_dest);
                enc_checknullref(i, v_chars);

                /* set up actdest */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actdest, v_dest, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_destposadj, v_destPos, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_destposadj, v_destposadj, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_destposadj, v_destposadj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, v_destposadj));

                /* set up length */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_length, var.ContentsOf(v_chars, GetArrayFieldOffset(ArrayFields.sizes)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_length, var.ContentsOf(v_length), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i_i4sx, v_length, v_length, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_length, v_length, var.Const(2)));

                /* set up actsrc */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actsrc, var.ContentsOf(v_chars, GetArrayFieldOffset(ArrayFields.inner_array)), var.Null));

                /* call memmove */
                i.tacs.Add(new CallEx(var.Null, new var[] { v_actdest, v_actsrc, v_length }, "__memmove", callconv_memmove));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M14_InternalStrcpy_S16_0005010E080E0808")
            {
                /* static void InternalStrcpy(string dest, int32 destPos, string src, int32 sPos, int32 count) */

                var v_dest = i.stack_before[i.stack_before.Count - 5].contains_variable;
                var v_destPos = i.stack_before[i.stack_before.Count - 4].contains_variable;
                var v_src = i.stack_before[i.stack_before.Count - 3].contains_variable;
                var v_sPos = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_count = i.stack_before[i.stack_before.Count - 1].contains_variable;

                var v_actdest = state.next_variable++;
                var v_actsrc = state.next_variable++;
                var v_length = state.next_variable++;
                var v_destposadj = state.next_variable++;
                var v_srcposadj = state.next_variable++;

                enc_checknullref(i, v_dest);
                enc_checknullref(i, v_src);

                /* set up actdest */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actdest, v_dest, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_destposadj, v_destPos, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_destposadj, v_destposadj, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_destposadj, v_destposadj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, v_destposadj));

                /* set up length */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_length, v_count, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_length, v_length, var.Const(2)));

                /* set up actsrc */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actsrc, v_src, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actsrc, v_actsrc, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_srcposadj, v_sPos, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_srcposadj, v_srcposadj, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_srcposadj, v_srcposadj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actsrc, v_actsrc, v_srcposadj));

                /* call memmove */
                i.tacs.Add(new CallEx(var.Null, new var[] { v_actdest, v_actsrc, v_length }, "__memmove", callconv_memmove));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M14_InternalStrcpy_S12_0003010E080E")
            {
                /* static void InternalStrcpy(string dest, int32 destPos, string src) */

                var v_dest = i.stack_before[i.stack_before.Count - 3].contains_variable;
                var v_destPos = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_src = i.stack_before[i.stack_before.Count - 1].contains_variable;

                var v_actdest = state.next_variable++;
                var v_actsrc = state.next_variable++;
                var v_length = state.next_variable++;
                var v_destposadj = state.next_variable++;

                enc_checknullref(i, v_dest);
                enc_checknullref(i, v_src);

                /* set up actdest */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actdest, v_dest, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_destposadj, v_destPos, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_destposadj, v_destposadj, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_destposadj, v_destposadj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, v_destposadj));

                /* set up length */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_length, var.ContentsOf(v_src, GetStringFieldOffset(StringFields.length)), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_length, v_length, var.Const(2)));

                /* set up actsrc */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actsrc, v_src, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actsrc, v_actsrc, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));

                /* call memmove */
                i.tacs.Add(new CallEx(var.Null, new var[] { v_actdest, v_actsrc, v_length }, "__memmove", callconv_memmove));
            }
            else if (methname == "_N6_System_T6_String_S2_0E_M14_InternalStrcpy_S18_0005010E081D030808")
            {
                /* static void InternalStrcpy(string dest, int32 destPos, char[] chars, int32 sPos, int32 count) */

                var v_dest = i.stack_before[i.stack_before.Count - 5].contains_variable;
                var v_destPos = i.stack_before[i.stack_before.Count - 4].contains_variable;
                var v_chars = i.stack_before[i.stack_before.Count - 3].contains_variable;
                var v_sPos = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_count = i.stack_before[i.stack_before.Count - 1].contains_variable;

                var v_actdest = state.next_variable++;
                var v_actsrc = state.next_variable++;
                var v_length = state.next_variable++;
                var v_destposadj = state.next_variable++;
                var v_srcposadj = state.next_variable++;

                enc_checknullref(i, v_dest);
                enc_checknullref(i, v_chars);

                /* set up actdest */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actdest, v_dest, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, var.Const(new IntPtr(GetStringFieldOffset(StringFields.data_offset)))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_destposadj, v_destPos, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_destposadj, v_destposadj, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_destposadj, v_destposadj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actdest, v_actdest, v_destposadj));

                /* set up length */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_length, v_count, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_length, v_length, var.Const(2)));

                /* set up actsrc */
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_actsrc, v_actsrc, var.ContentsOf(v_chars, GetArrayFieldOffset(ArrayFields.inner_array))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_srcposadj, v_sPos, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.mul_i4, v_srcposadj, v_srcposadj, var.Const(2)));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_srcposadj, v_srcposadj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_actsrc, v_actsrc, v_srcposadj));

                /* call memmove */
                i.tacs.Add(new CallEx(var.Null, new var[] { v_actdest, v_actsrc, v_length }, "__memmove", callconv_memmove));
            }
            else if (methname == "_N6_System_T4_Type_S6_1284F8_M20_internal_from_handle_S12_00011284F818")
            {
                /* static System.Type internal_from_handle(IntPtr handle.Value)
                 * 
                 * In this case the argument should already be an instance of tysos.TysosType - we therefore
                 * merely have to check it for null and return it.
                 * 
                 */

                var v_handle = i.stack_before[i.stack_before.Count - 1].contains_variable;
                enc_checknullref(i, v_handle);
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++, v_handle, var.Null));
            }
            else if (methname == "_N6_System_T6_Object_S2_1C_M7_GetType_S10_20001284F8")
            {
                // this is instance System.Type GetType()
                /* To get an instance of tysos.TysosType, we dereference the object to get its VTable, then dereference it again */

                var v_obj = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_vtbl = state.next_variable++;
                enc_checknullref(i, v_obj);
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_vtbl, var.ContentsOf(v_obj, 0, GetSizeOfPointer()), var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, state.next_variable++, var.ContentsOf(v_vtbl, 0, GetSizeOfPointer()), var.Null));


                /* i.tacs.Add(new CallEx(state.next_variable++, new var[] { i.stack_before[i.stack_before.Count - 1].contains_variable }, ThreeAddressCode.Op.call_i, "__ty_gettype",
                    MakeStaticCall("default", new Signature.Param(BaseType_Type.I), new List<Signature.Param> { new Signature.Param(BaseType_Type.I) }, ThreeAddressCode.Op.call_i))); */
            }
            else if (methname == "_N34_System#2ERuntime#2EInteropServices_T7_Marshal_S6_128E94_M37_GetFunctionPointerForDelegateInternal_S12_00011812811C")
            {
                /* static IntPtr GetFunctionPointerForDelegateInternal(System.Delegate)
                 * 
                 * 
                 * assign_from_virtftn_ptr(delegate + field_offset)
                 */

                if (provides)
                    return true;

                Layout l = Layout.GetLayout(new TypeToCompile(i.stack_before[i.stack_before.Count - 1].type, this), this);
                int method_ptr_offset = l.GetField("VirtFtnPtr method_ptr", false).offset;
                var obj_id = i.stack_before[i.stack_before.Count - 1].contains_variable;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_from_virtftnptr_ptr, state.next_variable++, var.ContentsOf(obj_id, method_ptr_offset), var.Null));
            }
            else if ((methname == "_N6_System_T11_Environment_S6_12843C_M11_get_NewLine_S6_00000E") ||
                (methname == "_N18_System.Diagnostics_T10_StackTrace_S6_128768_M9_get_trace_S22_00031D1287641282B80802") ||
                (methname == "_N6_System_T6_String_S2_0E_M19_InternalAllocateStr_S8_00010E08") ||
                (methname == "_N31_System.Runtime.CompilerServices_T14_RuntimeHelpers_S6_128D24_M22_get_OffsetToStringData_S6_000008") ||
                (methname == "_N6_System_T4_Type_S6_1284F8_M20_internal_from_handle_S12_00011284F818") ||
                (methname == "_N6_System_T6_String_S2_0E_M14_InternalStrcpy_S16_0005010E080E0808") ||
                (methname == "_N6_System_T6_String_S2_0E_M12_InternalJoin_S16_00040E0E1D0E0808") ||
                (methname == "_N6_System_T5_Array_S6_12831C_M13_GetLowerBound_S8_20010808") ||
                (methname == "_N6_System_T4_Type_S6_1284F8_M16_IsInstanceOfType_S8_2001021C") ||
                (methname == "_N6_System_T5_Array_S6_12831C_M9_GetLength_S8_20010808") ||
                (methname == "_N6_System_T5_Array_S6_12831C_M8_FastCopy_S24_00050212831C0812831C0808") ||
                (methname == "_N6_System_T9_ValueType_S4_1124_M19_InternalGetHashCode_S14_0002081C101D1C") ||
                (methname == "_N6_System_T5_Array_S6_12831C_M7_GetRank_S6_200008") ||
                (methname == "_N6_System_T5_Array_S6_12831C_M12_SetValueImpl_S10_2002011C08"))
            {
                // List methods that are not implemented directly by tysila, but should be handled by the system
                handled = false;
                return handled;
            }
            else if (methname == "_N3_ABI_T15_OtherOperations_Special_M4_Halt_S6_000001")
            {
                i.tacs.Add(new CallEx(var.Null, new var[] { }, "__halt", MakeStaticCall("default", new Signature.Param(BaseType_Type.Void), new List<Signature.Param>(), ThreeAddressCode.Op.call_void)));
            }
            else if (methname == "_N6_System_T4_Math_S6_1284BC_M4_Sqrt_S8_00010D0D")
            {
                // static double Sqrt(double x)
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.sqrt_r8, state.next_variable++, i.stack_before[i.stack_before.Count - 1].contains_variable, var.Null));
            }
            else if (methname == "_N18_System#2EThreading_T6_Thread_S6_129AAC_M22_CurrentThread_internal_S10_0000129AAC")
            {
                /* static System.Threading.Thread CurrentThread_internal()
                 * 
                 * Create a new System.Threading.Thread object, and assign the current thread id to its 'thread_id' field */

                Assembler.TypeToCompile thread_ttc = Metadata.GetTTC("mscorlib", "System.Threading", "Thread", this);
                Layout l = Layout.GetLayout(thread_ttc, this);

                var v_ret = state.next_variable++;
                var v_objid = state.next_variable++;
                var v_threadid = state.next_variable++;

                i.tacs.Add(new CallEx(v_ret, new var[] { var.Const(l.ClassSize) }, "gcmalloc", callconv_gcmalloc));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, var.ContentsOf(v_ret, l.GetFirstInstanceField("__vtbl").offset), var.AddrOfObject(l.vtable_object_name), var.Null));
                i.tacs.Add(new CallEx(v_objid, new var[] {}, "__get_new_obj_id", callconv_getobjid));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, var.ContentsOf(v_ret, l.GetFirstInstanceField("__object_id").offset), v_objid, var.Null));
                i.tacs.Add(new CallEx(v_threadid, new var[] { }, "__get_cur_thread_id", callconv_getcurthreadid));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.conv_i4_uzx, v_threadid, v_threadid, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i8, var.ContentsOf(v_ret, l.GetFirstInstanceField("thread_id").offset), v_threadid, var.Null));

                i.pushes_variable = v_ret;
                i_pushes_set = true;
            }
            else if (methname == "_N18_System#2EThreading_T7_Monitor_S6_129A80_M17_Monitor_try_enter_S10_0002021C08")
            {
                /* static bool Monitor_try_enter(object obj, int ms)
                 * 
                 * Try and acquire the mutex lock on object obj, waiting a maximum of ms milliseconds
                 * if ms == System.Threading.Timeout.Infinite then wait forever
                 */

                /* Currently we do not honour the ms argument
                 * If it is 0 then just try one and return success/failure
                 * Otherwise try infinitely
                 * 
                 * return true if we were successful, else false
                 * 
                 * code is:
                 * 
                 * mutex_lock_addr = obj + offset(mutex_lock)
                 * ret = false
                 * thread_id = call(__get_cur_thread_id)
                 * L1:
                 * ret = try_acquire(mutex_lock_addr, thread_id) (ret is global var here)
                 * cmp(ret, 0)
                 * bne L2
                 * cmp(ms, 0)
                 * bne L1
                 * L2:
                 * 
                 */

                var v_obj = i.stack_before[i.stack_before.Count - 2].contains_variable;
                var v_ms = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_mutex_lock_addr = state.next_variable++;
                var v_ret = state.next_variable++;
                var v_thread_id = state.next_variable++;

                int blk_loop = state.next_block++;
                int blk_success = state.next_block++;

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_mutex_lock_addr, v_obj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_mutex_lock_addr, v_mutex_lock_addr, var.Const(GetStringFieldOffset(StringFields.mutex_lock))));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i4, v_ret, var.Const(0), var.Null));
                i.tacs.Add(new CallEx(v_thread_id, new var[] { }, "__get_cur_thread_id", callconv_getcurthreadid));

                i.tacs.Add(LabelEx.LocalLabel(blk_loop));
                var v_globalret = v_ret;
                v_globalret.is_global = true;
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.try_acquire_i4, v_ret, v_mutex_lock_addr, v_thread_id));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_ret, var.Const(0), var.Null));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.bne, blk_success));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.cmp_i4, var.Null, v_ms, var.Const(0), var.Null));
                i.tacs.Add(new BrEx(ThreeAddressCode.Op.bne, blk_loop));

                i.tacs.Add(LabelEx.LocalLabel(blk_success));

                i.pushes_variable = v_ret;
                i_pushes_set = true;
            }
            else if (methname == "_N18_System#2EThreading_T7_Monitor_S6_129A80_M12_Monitor_exit_S8_0001011C")
            {
                /* static void Monitor_exit(object obj)
                 * 
                 * Release the mutex lock on obj, if we own it
                 */

                /* code is:
                 * 
                 * mutex_lock_addr = obj + offset(mutex_lock)
                 * thread_id = call(__get_cur_thread_id)
                 * release(mutex_lock_addr, thread_id)
                 * 
                 */

                var v_obj = i.stack_before[i.stack_before.Count - 1].contains_variable;
                var v_mutex_lock_addr = state.next_variable++;
                var v_thread_id = state.next_variable++;

                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.assign_i, v_mutex_lock_addr, v_obj, var.Null));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.add_i, v_mutex_lock_addr, v_mutex_lock_addr, var.Const(GetStringFieldOffset(StringFields.mutex_lock))));
                i.tacs.Add(new CallEx(v_thread_id, new var[] { }, "__get_cur_thread_id", callconv_getcurthreadid));
                i.tacs.Add(new ThreeAddressCode(ThreeAddressCode.Op.release_i4, var.Null, v_mutex_lock_addr, v_thread_id));
            }
            else
            {
                /*
#if !DEBUG
                if(mdr.IsInternalCall)
                    throw new NotImplementedException(methname);
#endif
                 */
                handled = false;
                return handled;
            }

            if (!(msig is Signature.Method))
            {
                throw new NotSupportedException();
            }

            Signature.Method msigm = msig as Signature.Method;
            i.pushes = msigm.RetType;
            if (i.pop_count < 0)
            {
                i.pop_count = msigm.Params.Count;
                if (msigm.HasThis && !msigm.ExplicitThis)
                    i.pop_count++;
            }
            if (!i_pushes_set && (msigm.RetType.CliType(this) != CliType.void_))
                i.pushes_variable = state.next_variable - 1;

            return handled;
        }
    }
}
